﻿#include "mygraphicsview.h"
#include <QGraphicsLineItem>
#include <QGraphicsRectItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsTextItem>
#include <QGraphicsSceneMouseEvent>
#include <QtMath>
#include <QDebug>

class TextItem: public QGraphicsTextItem
{
public:
    enum { Type = UserType + 1 };

     int type() const
     {
         // Enable the use of qgraphicsitem_cast with this item.
         return Type;
     }

    TextItem()
    {
        setPlainText(QStringLiteral("文本"));

        setFlags(QGraphicsItem::ItemIsMovable    |
                 QGraphicsItem::ItemIsFocusable  |
                 QGraphicsItem::ItemIsSelectable);
        setTextInteractionFlags(Qt::TextEditorInteraction);
    }
//    virtual void paint(QPainter* painter,
//                       const QStyleOptionGraphicsItem* option,
//                       QWidget* widget = NULL)
//    {
//        QGraphicsTextItem::paint(painter, option, widget);
//    }

protected:
    void focusOutEvent (QFocusEvent * event) override
    {
        Q_UNUSED(event);
        setTextInteractionFlags(Qt::NoTextInteraction);
        QGraphicsTextItem::focusOutEvent(event);
    }

    void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event) override
    {
        Q_UNUSED(event);
        setTextInteractionFlags(Qt::TextEditorInteraction); // TextEditorInteraction
        setFocus(Qt::MouseFocusReason);
        QGraphicsTextItem::mouseDoubleClickEvent(event);
    }

    void mousePressEvent(QGraphicsSceneMouseEvent* event) override {
        event->accept();
        QGraphicsTextItem::mousePressEvent(event);
    }

//    void keyPressEvent(QKeyEvent *event) override {

//    }
};

class ArrowItem: public QGraphicsLineItem
{
public:
    enum { Type = UserType + 2 };

     int type() const
     {
         // Enable the use of qgraphicsitem_cast with this item.
         return Type;
     }

    ArrowItem()
    {
        setFlags(QGraphicsItem::ItemIsMovable    |
                 QGraphicsItem::ItemIsFocusable  |
                 QGraphicsItem::ItemIsSelectable);
    }

    virtual void paint(QPainter* painter,
                       const QStyleOptionGraphicsItem* option,
                       QWidget* widget = NULL)
    {
        auto line = this->line();
        painter->setPen(this->pen());
        drawarrow(line.p1(),line.p2(),painter);

        //QGraphicsLineItem::paint(painter, option, widget);
    }

protected:
    void drawarrow(QPointF startpoint, QPointF endpoint, QPainter *p)
    {
        int diffX = qAbs(endpoint.x() - startpoint.x());
        int diffY = qAbs(endpoint.y()- startpoint.y());

        qreal diff = qSqrt(diffX * diffX + diffY * diffY);
        if(diff < 20.0){
            return;
        }

        double par = 15.0;
        double slopy = qAtan2((endpoint.y()-startpoint.y()),(endpoint.x()-startpoint.x()));
        double cosy= qCos(slopy);
        double siny= qSin(slopy);
        QPointF point1 = QPointF(endpoint.x() + int(-par*cosy - (par / 2.0*siny)), endpoint.y() + int(-par*siny + (par / 2.0*cosy)));
        QPointF point2 = QPointF(endpoint.x() + int(-par*cosy + (par / 2.0*siny)), endpoint.y() - int(par / 2.0*cosy + par*siny));
        QPointF points[3]={endpoint,point1,point2};
        p->setRenderHint(QPainter::Antialiasing,true);
        p->drawPolygon(points,3);

        int offsetx=int(par*siny/3);
        int offsety=int(par*cosy/3);
        QPointF point3,point4;
        point3 = QPointF(endpoint.x() + int(-par*cosy - (par / 2.0*siny)) + offsetx, endpoint.y() + int(-par*siny + (par / 2.0*cosy)) - offsety);
        point4 = QPointF(endpoint.x() + int(-par*cosy + (par / 2.0*siny) - offsetx), endpoint.y() - int(par / 2.0*cosy + par*siny) + offsety);
        QPointF arrbodypoints[3]={startpoint,point3,point4};
        //p->drawPolygon(arrbodypoints,3);
        p->drawLine(startpoint,endpoint);
    }

//    void drawarrow2(QVector<QPoint> points,QPainter &p)
//    {
//        if(points.size() < 2) {
//            return;
//        }

//        //    int diffX = qAbs(endpoint.x() - startpoint.x());
//        //    int diffY = qAbs(endpoint.y()- startpoint.y());

//        //    qreal diff = qSqrt(diffX * diffX + diffY * diffY);
//        //    if(diff < ARROW_MINSIZE){
//        //        return;
//        //    }

//        for(int i=0;i<points.size()-1;++i){
//            auto p1 = points.at(i);
//            auto p2 = points.at(i+1);
//            p.drawLine(p1,p2);
//        }

//        QPoint startpoint = points.at(points.size()-2);
//        QPoint endpoint = points.at(points.size()-1);

//        drawarrow(startpoint,endpoint,p);
//    }

};

MyGraphicsView::MyGraphicsView(QWidget* parent)
    : QGraphicsView(parent){
    setViewportUpdateMode(SmartViewportUpdate);

    setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

    setMode(m_mode);
}

void MyGraphicsView::doScale(qreal s){
    scale(s,s);
}

#include <QTextCursor>
QGraphicsItem* MyGraphicsView::addText(QPoint scenePos,QString txt,QFont font){
    QGraphicsScene* scene = this->scene();
    if(scene){
        TextItem* txtItem = new TextItem();

        QFont f;
        f.setPointSize(penSize());
        f.setBold(true);
        f.setFamily("msyh");
        txtItem->setFont(f);
        txtItem->setDefaultTextColor(penColor());

        scene->addItem(txtItem);
        txtItem->setPos(scenePos);
        txtItem->setFocus();
        QTextCursor cursor = txtItem->textCursor();
        cursor.select(QTextCursor::Document);
        cursor.clearSelection();
        txtItem->setTextCursor(cursor);
        return txtItem;
    }

    return nullptr;
}

void MyGraphicsView::wheelEvent(QWheelEvent* event) {
    if(/*Select != m_mode ||*/ event->modifiers() != Qt::AltModifier){
        return;
    }

    double scaleFactor = 1.15; //How fast we zoom

    if(event->delta() > 0 ){
        doScale(scaleFactor);
    }else{
        doScale(1.0/scaleFactor);
    }

    event->accept();
}

void MyGraphicsView::keyPressEvent(QKeyEvent *event)
{
    auto key = event->key();
    if(key == Qt::Key_Delete) {
        QList<QGraphicsItem *> items = scene()->selectedItems();
        foreach(auto item,items) {
            if(item){
                scene()->removeItem(item);
            }
        }
    }else if(key == Qt::Key_Left){
        QList<QGraphicsItem *> items = scene()->selectedItems();
        if(items.size()>0){
            event->accept();
        }
        foreach(auto item,items) {
            if(item){
                QPointF pos = item->pos();
                if(pos.x() > 1){
                    pos.setX(pos.x()-1);
                    item->setPos(pos);
                }
            }
        }
    }else if(key == Qt::Key_Right){
        QList<QGraphicsItem *> items = scene()->selectedItems();
        if(items.size()>0){
            event->accept();
        }
        foreach(auto item,items) {
            if(item){
                QPointF pos = item->pos();
                if(pos.x() < scene()->width() - 1){
                    pos.setX(pos.x()+1);
                    item->setPos(pos);
                }
            }
        }
    }else if(key == Qt::Key_Up){
        QList<QGraphicsItem *> items = scene()->selectedItems();
        if(items.size()>0){
            event->accept();
        }
        foreach(auto item,items) {
            if(item){
                QPointF pos = item->pos();
                if(pos.y() > 1){
                    pos.setY(pos.y()-1);
                    item->setPos(pos);
                }
            }
        }
    }else if(key == Qt::Key_Down){
        QList<QGraphicsItem *> items = scene()->selectedItems();
        if(items.size()>0){
            event->accept();
        }
        foreach(auto item,items) {
            if(item){
                QPointF pos = item->pos();
                if(pos.y() < scene()->height() - 1){
                    pos.setY(pos.y()+1);
                    item->setPos(pos);
                }
            }
        }
    }
}

void MyGraphicsView::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        m_isPressed = true;
        m_pressPos = this->mapToScene(event->pos()).toPoint();

        qDebug() << m_pressPos << m_mode << penSize() << penColor();

        QPen pen;
        pen.setColor(penColor());
        pen.setWidth(penSize());
        if(m_mode == DrawStraightLine) {
            auto item = this->scene()->addLine(m_pressPos.x(),m_pressPos.y(),m_pressPos.x(),m_pressPos.y(),pen);
            item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);

            setCurrentItem(item);
            m_items.push_back(item);
        }else if(m_mode == DrawLine) {
            QPainterPath path(m_pressPos);
            auto item = this->scene()->addPath(path,pen);
            item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);

            setCurrentItem(item);
            m_items.push_back(item);
        }else if(m_mode == DrawRect) {
            auto item = this->scene()->addRect(m_pressPos.x(),m_pressPos.y(),1,1,pen);
            item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);

            setCurrentItem(item);
            m_items.push_back(item);
        }else if(m_mode == DrawRound) {
            auto item = this->scene()->addEllipse(m_pressPos.x(),m_pressPos.y(),1,1,pen);
            item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);

            setCurrentItem(item);
            m_items.push_back(item);
        }else if(m_mode == DrawArrow){
            auto item = new ArrowItem();
            item->setPen(pen);
            item->setLine(m_pressPos.x(),m_pressPos.y(),m_pressPos.x(),m_pressPos.y());
            this->scene()->addItem(item);
            item->setFlags(QGraphicsItem::ItemIsMovable|QGraphicsItem::ItemIsSelectable);

            setCurrentItem(item);
            m_items.push_back(item);
        }else if(m_mode == DrawText) {
            TextItem* lastTextItem = qgraphicsitem_cast<TextItem*>(currentItem());
            if(lastTextItem){
                auto pos = lastTextItem->mapFromScene(m_pressPos);
                if(lastTextItem->contains(pos)) { //点击了同一个文本对象,应该忽略该点击事件,交给textitem自己处理
                    event->accept();
                    return;
                }
            }

            auto item = addText(m_pressPos,"test",QFont());
            setCurrentItem(item);
            m_items.push_back(item);
        }
    }

    if(!m_canUndo) {
        m_canUndo = !m_items.isEmpty();
        emit canUndoChanged(m_canUndo);
    }

    if(m_mode != Select && m_mode != DrawText){
        return;
    }

    QGraphicsView::mousePressEvent(event);
}

void MyGraphicsView::mouseMoveEvent(QMouseEvent *event)
{
    if(m_isPressed) {
        auto pos = event->pos();
        auto scenePos = this->mapToScene(pos);

        if(m_mode == DrawStraightLine) {
            auto curItem = currentItem();
            QGraphicsLineItem* lineItem = qgraphicsitem_cast<QGraphicsLineItem*>(curItem);
            lineItem->setLine(m_pressPos.x(),m_pressPos.y(), scenePos.x(),scenePos.y());
        }else if(m_mode == DrawLine) {
            auto curItem = currentItem();
            QGraphicsPathItem* pathItem = qgraphicsitem_cast<QGraphicsPathItem*>(curItem);
            auto path = pathItem->path();
            if(path.isEmpty()) {
                path.moveTo(m_pressPos);
            }

            path.lineTo(scenePos.x(),scenePos.y());
            pathItem->setPath(path);
        }else if(m_mode == DrawRect) {
            auto curItem = currentItem();
            QGraphicsRectItem* rectItem = qgraphicsitem_cast<QGraphicsRectItem*>(curItem);
            auto rect = getRect(m_pressPos,scenePos.toPoint());
            rectItem->setRect(QRectF(rect));
        }else if(m_mode == DrawRound) {
            auto curItem = currentItem();
            QGraphicsEllipseItem* roundItem = qgraphicsitem_cast<QGraphicsEllipseItem*>(curItem);
            auto rect = getRect(m_pressPos,scenePos.toPoint());
            roundItem->setRect(QRectF(rect));
        }else if(m_mode == DrawArrow) {
            auto curItem = currentItem();
            ArrowItem* arrowItem = qgraphicsitem_cast<ArrowItem*>(curItem);
            arrowItem->setLine(m_pressPos.x(),m_pressPos.y(), scenePos.x(),scenePos.y());
        }
    }
    QGraphicsView::mouseMoveEvent(event);
}

void MyGraphicsView::mouseReleaseEvent(QMouseEvent *event)
{
    m_isPressed = false;
    if(event->button() == Qt::LeftButton) {
        auto pos = event->pos();
    }

    QGraphicsView::mouseReleaseEvent(event);
}

void MyGraphicsView::mouseDoubleClickEvent(QMouseEvent* event) {
//    QList<QGraphicsItem *> items = scene()->selectedItems();
//    if(items.size() != 1) {
//        return;
//    }

//    QGraphicsItem* item = items.at(0);
//    event->accept();
//    emit doubleClickedItem(item);
    return QGraphicsView::mouseDoubleClickEvent(event);
}

QColor MyGraphicsView::penColor() const
{
    return m_penColor;
}

void MyGraphicsView::setPenColor(const QColor &penColor)
{
    m_penColor = penColor;

    auto curItem = currentItem();
    TextItem* textItem = qgraphicsitem_cast<TextItem*>(curItem);
    if(textItem) {
        textItem->setDefaultTextColor(m_penColor);
    }
}

QRect MyGraphicsView::getRect(QPoint p1, QPoint p2)
{
    int xx = p1.x() < p2.x() ? p1.x() : p2.x();
    int yy = p1.y() < p2.y() ? p1.y() : p2.y();

    return QRect(xx, yy,abs(p2.x() - p1.x()), abs(p2.y() - p1.y()));
}

void MyGraphicsView::undo()
{
    if(m_items.isEmpty()){
        return;
    }

    auto item = m_items.takeLast();
    if(item) {
        this->scene()->removeItem(item);
    }

    if(m_items.isEmpty()) {
        m_canUndo = false;
        emit canUndoChanged(m_canUndo);
    }
}

int MyGraphicsView::penSize() const
{
    return m_penSize;
}

void MyGraphicsView::setPenSize(int penSize)
{
    m_penSize = penSize;

    auto curItem = currentItem();
    TextItem* textItem = qgraphicsitem_cast<TextItem*>(curItem);
    if(textItem) {
        auto font = textItem->font();
        font.setPointSize(m_penSize);
        textItem->setFont(font);
    }
}

MyGraphicsView::Mode MyGraphicsView::mode() const
{
    return m_mode;
}

void MyGraphicsView::setMode(Mode mode)
{
    m_mode = mode;

    if(m_mode != Select) {
        setDragMode(QGraphicsView::NoDrag);
        setCursor(Qt::ArrowCursor);
    }else{
        setDragMode(QGraphicsView::ScrollHandDrag);
        setCursor(Qt::CrossCursor);
    }
}

QGraphicsItem *MyGraphicsView::currentItem() const
{
    return m_currentItem;
}

void MyGraphicsView::setCurrentItem(QGraphicsItem *currentItem)
{
    m_currentItem = currentItem;
}
